package com.jf.concurrent.thread;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 线程依次交替执行，
 * <p>
 * 方法一：useJoin      使用线程join实现, 无法实现循环执行
 * 方法二：useLockCondition 使用锁的等待唤醒实现，
 * 方法三：useSemaphore 使用信号量控制多个线程依次交替执行.
 *
 * @author Junfeng
 */
public class ThreadExecuteSequentially {

    private static AtomicInteger num = new AtomicInteger();
    private static AtomicInteger cn = new AtomicInteger();

    public static void main(String[] arg) throws InterruptedException {

//        useSemaphore(3);
//        useJoin(5);

        useLockCondition(3);
    }

    public static void useJoin(int n) throws InterruptedException {
        Thread[] threads = new Thread[n];
        for (int i = 0; i < n; i++) {
            threads[i] = new Thread("T-" + (i + 1));
        }
        JoinTask[] joinTasks = new JoinTask[n];
        for (int i = 0; i < threads.length; i++) {
            int index = i + 1;
            if (index == threads.length) {
                index = 0;
            }
            joinTasks[i] = new JoinTask(threads[index]);
        }
        for (JoinTask joinTask : joinTasks) {
            joinTask.start();
        }

        while (ThreadExecuteSequentially.num.get() < 100) {
            TimeUnit.SECONDS.sleep(1);
        }
    }

    static class JoinTask extends Thread {

        private Thread next;

        public JoinTask(Thread next) {
            this.next = next;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + ": " + ThreadExecuteSequentially.num.incrementAndGet());
            try {
                next.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void useLockCondition(int n) {
        Lock lock = new ReentrantLock();

        Condition[] conditions = new Condition[n];
        for (int i = 0; i < n; i++) {
            conditions[i] = lock.newCondition();
        }

        for (int i = 0; i < n; i++) {
            int next = i + 1;
            if (next == n) {
                next = 0;
            }
            new ConditionTask(i, lock, conditions[i], conditions[next]).start();
        }

    }

    static class ConditionTask extends Thread {

        private int i;
        private Lock lock;
        private Condition condition;
        private Condition next;

        public ConditionTask(int i, Lock lock, Condition condition, Condition next) {
            this.i = i;
            this.lock = lock;
            this.condition = condition;
            this.next = next;
        }

        @Override
        public void run() {
            lock.lock();
            try {
                if (i != cn.get()) {
                    condition.await();
                } else {
                    System.out.println(Thread.currentThread().getName() + ": " + ThreadExecuteSequentially.num.incrementAndGet());
                    cn.incrementAndGet();
                    next.signal();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    /**
     * 线程依次交替执行 .</p>
     *
     * <p>CreateTime: 2019/3/26.</p>
     *
     * @param n 线程数
     * @author Junfeng
     */
    public static void useSemaphore(int n) throws InterruptedException {
        Thread[] threads = new Thread[n];
        final Semaphore[] syncObjects = new Semaphore[n];
        for (int i = 0; i < n; i++) {
            syncObjects[i] = new Semaphore(1);
            if (i != n - 1) {
                syncObjects[i].acquire();
            }
        }
        for (int i = 0; i < n; i++) {
            final Semaphore lastSemphore = i == 0 ? syncObjects[n - 1] : syncObjects[i - 1];
            final Semaphore curSemphore = syncObjects[i];
            final int index = i;
            new Thread(() -> {
                try {
                    while (true) {
                        // 获取前一个的 信号量，如果前一个释放了则 当前开始执行，并释放当前信号量，提供下一线程执行
                        lastSemphore.acquire();
                        System.out.println("thread" + index + ": " + ThreadExecuteSequentially.num.incrementAndGet());
                        if (ThreadExecuteSequentially.num.get() > 100) {
                            System.exit(0);
                        }
                        curSemphore.release();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }).start();
        }
    }
}
